/* eslint-disable no-loop-func */
import { Box, Button, Divider, Frame, HorizontalStack, Text, TopBar, VerticalStack } from '@shopify/polaris'
import React, { useEffect, useRef } from 'react'
import api from '../api'
import issuesApi from '@/apps/dashboard/pages/issues/api'
import func from '@/util/func'
import { useState } from 'react'
import "./styles.css"
import ReportTOC from './ReportTOC'
import { useParams, useSearchParams } from 'react-router-dom'
import transform from '../transform';
import ReportHeader from './ReportHeader'
import ReportSummary from './ReportSummary'
import ReportFindings from './ReportFindings'
import ReportConclusion from './ReportConclusion'
import ReportFooter from './ReportFooter'
import reportTransform from './transform'
import LocalStore from '../../../../main/LocalStorageStore'

const VulnerabilityReport = () => {
    const [loading, setLoading] = useState(true)
    const [pdfDownloadEnabled, setPdfDownloadEnabled] = useState(false)
    const [categoryVsIssuesMap, setCategoryVsIssuesMap] = useState({})
    const [categoryMap, setCategoryMap] = useState({})
    const [severitiesCount, setSeveritiesCount] = useState({ HIGH: 0, MEDIUM: 0, LOW: 0 })
    const [categoryVsApisCountMap, setCategoryVsApisCountMap] = useState({})
    const [aktoFindingsTableData, setAktoFindingsTableData] = useState([])
    const [ aktoRecommendations, setAktoRecommendations ] = useState([])
    const [totalIssues, setTotalIssues] = useState(0)
    const [severityMap, setSeverityMap] = useState({})
    const [scanTime, setScanTime] = useState('')
    const [totalApisTested, setTotalApisTested] = useState(0)
    const [subCategoryMap, setSubCategoryMap] = useState({})
    const [reportTestConfigurationData, setReportTestConfigurationData] = useState({})
    const [topCategoriesChartData, setTopCategoriesChartData] = useState({})
    const [hostNameVsSeverityMap, setHostNameVsSeverityMap] = useState({})

    const pdfRef = useRef()
    const params = useParams()
    const reportId = params.reportId

    const [searchParams] = useSearchParams();
    const summaryMode = searchParams.get('summaryMode') === 'true' || false;

    const [organizationName, setOrganizationName] = useState(func.capitalizeFirstLetter(window.ACCOUNT_NAME || ""))
    const [currentDate, setCurrentDate] = useState('-')
    const [userName, setUserName] = useState(func.capitalizeFirstLetter(window.USER_NAME.split('@')[0] || ""))

    const aktoFindingsChildren = []
    Object.keys(categoryVsIssuesMap).forEach((categoryName, index) => {
        const issuesArr = categoryVsIssuesMap[categoryName]
        issuesArr.forEach((issue) => {
            aktoFindingsChildren.push({
                text: issue.testName,
                link: issue.testName,
                children: null,
            })
        })
    })

    const tocList = [
        { text: "Report summary", link: "report-summary", children: null },
        { text: (<>Akto findings for <span id='organization-name'>{organizationName}</span></>), link: "findings-table", children: aktoFindingsChildren },
        { text: "Conclusion and Next Steps", link: "akto-next-step", children: null },
    ]

    const fetchSubcategoriesAndCategories = async () => {
        const metaDataObj = await transform.getAllSubcategoriesData(true, "runTests")
        return [metaDataObj.subCategories, metaDataObj.categories]
    }

    const fetchReportTestConfigurationData  = async (testingRunHexId, testingRunResultSummary, subCategoryMap) => {
        const { testingRun, testingRunType } =  await api.fetchTestingRunResultSummaries(testingRunHexId, Date.now(), Date.now())
        const { testRoles } = await api.fetchTestRoles()
        const uniqueHostsTested = await api.getUniqueHostsTested(testingRunHexId)

        let testRole = null
        const testRoleHexId = testingRun?.testingRunConfig?.testRoleId || ""
        
        if (testRoles && testRoleHexId.length > 0) {
            testRole = testRoles.find((role) => role.hexId === testRoleHexId)
        }

        const reportTestConfigurationData = reportTransform.prepareReportTestConfigurationData(testingRun, testingRunType, testRole, testingRunResultSummary, uniqueHostsTested, subCategoryMap)
        return reportTestConfigurationData
    }

    const fetchVulnerableData = async () => {
        let resultsCount = 0;
        let vulnerableTestingRunResults = []
        let currentFilters = {}
        let issueIdsFromFilter = []
        let testingRunHexId = null
        let testingRunResultSummary = null

        await api.getReportFilters(reportId).then((res) => {
            currentFilters = res.filtersForReport;
            if(res.issuesIdsForReport !== null && res.issuesIdsForReport !== undefined && res.issuesIdsForReport.length > 0){
                issueIdsFromFilter = res.issuesIdsForReport
            }
        })

        if (window.location.pathname.includes('testing')) {
            const testingRunSummaryId = currentFilters.testingRunResultSummaryId[0]
            
            testingRunResultSummary = await api.fetchTestingRunResultsSummary(testingRunSummaryId)

            if (testingRunResultSummary == null) {
                setScanTime('-')
            } else if (testingRunResultSummary?.state === 'COMPLETED') {
                const time = func.getTimeTakenByTest(testingRunResultSummary?.startTimestamp, testingRunResultSummary?.endTimestamp)
                setScanTime(time || '-')
            } else {
                setScanTime("-")
            }

            if (testingRunResultSummary?.startTimestamp) {
                setCurrentDate(func.getFormattedDate(testingRunResultSummary?.startTimestamp))
            } else {
                setCurrentDate('-')
            }

            testingRunHexId = testingRunResultSummary?.testingRunHexId

            while (true) {
                let testingRunCountsFromDB = 0
                await api.fetchVulnerableTestingRunResults(testingRunSummaryId, resultsCount, currentFilters).then(async(resp) => {
                    let issueIds = resp.testingRunResults.map((x) => {
                        return {
                            apiInfoKey: x.apiInfoKey,
                            testErrorSource: "AUTOMATED_TESTING",
                            testSubCategory: x.testSubType
                        }
                    })
                    await issuesApi.fetchIssuesFromResultIds(issueIds, ["IGNORED", "FIXED"]).then((new_resp) => {
                        let nonVulTests = new Set((new_resp || []).map(x => 
                            x.id.apiInfoKey.url + "??##" + x.id.apiInfoKey.method + "??##" + x.id.apiInfoKey.apiCollectionId + "??##" + x.id.testSubCategory
                        ));
                        let filteredTests = resp.testingRunResults.filter((x) => {
                            let tempId = x.apiInfoKey.url + "??##" + x.apiInfoKey.method + "??##" + x.apiInfoKey.apiCollectionId + "??##" + x.testSubType
                            return !nonVulTests.has(tempId)
                        })
                        vulnerableTestingRunResults = [...vulnerableTestingRunResults, ...filteredTests]
                    })
                    testingRunCountsFromDB = resp.testingRunResults.length
                })
                resultsCount += 50
                if (testingRunCountsFromDB < 50) {
                    //EOF: break as no further documents exists
                    break
                }
            }
        } else {
            while (true) {
                let testingRunCountsFromDB = 0
                let issuesFilters = JSON.parse(JSON.stringify(currentFilters));
                issuesFilters.startEpoch = parseInt(currentFilters.startEpoch[0]);
                issuesFilters.endTimeStamp = parseInt(currentFilters.endTimeStamp[0]);
                issuesFilters.filterCollectionsId = currentFilters.filterCollectionsId.map((x) => {
                    if(x !== null && x.length > 0) return parseInt(x)
                })
                issuesFilters.filterCollectionsId = issuesFilters.filterCollectionsId.filter((x) => x !== undefined)
                issuesFilters.activeCollections = currentFilters.activeCollections[0] === 'true'


                await issuesApi.fetchVulnerableTestingRunResultsFromIssues(issuesFilters, issueIdsFromFilter, resultsCount).then(resp => {
                    const testRunResultsFromIssues = (resp.removedRunResultsIssuesList || [])?.map((issue) => {
                        return {
                            apiInfoKey: issue.id.apiInfoKey,
                            testResults: [{confidence: issue.severity, vulnerable: true}],
                            testSubType: issue.id.testSubCategory,
                            vulnerable: true
                        }
                    })

                    vulnerableTestingRunResults = [...vulnerableTestingRunResults, ...resp.testingRunResults, ...testRunResultsFromIssues]
                    testingRunCountsFromDB = resp.testingRunResults.length + testRunResultsFromIssues.length
                    //sampleDataVsCurlMap = { ...sampleDataVsCurlMap, ...resp.sampleDataVsCurlMap }
                })
                resultsCount += 50
                if (testingRunCountsFromDB < 50 || resultsCount >= 1000) {
                    //EOF: break as no further documents exists
                    break
                }
            }
        }
        const localCategoryMap = LocalStore.getState().categoryMap
        const localSubCategoryMap = LocalStore.getState().subCategoryMap
        let shouldFetchSubcategoriesAndCategories = false
        if (
            (!localCategoryMap || Object.keys(localCategoryMap).length === 0) ||
            (!localSubCategoryMap || Object.keys(localSubCategoryMap).length === 0)
        ) {
            shouldFetchSubcategoriesAndCategories = true
        }

        let subCategories
        let categories
        if(shouldFetchSubcategoriesAndCategories) {
            let res = await fetchSubcategoriesAndCategories()
            subCategories = res[0]
            categories = res[1]
        } else {
            subCategories = Object.values(localSubCategoryMap)
            categories = Object.values(localCategoryMap)
        }

        let subCategoryMap = {};
        subCategories.forEach((x) => {
            subCategoryMap[x.name] = x;
        });

        setSubCategoryMap(subCategoryMap)

        let categoryMap = {};
        categories.forEach((category) => {
            categoryMap[category.name] = category;
        });

        // Fetch report test configuration data now that we have subCategoryMap
        // Only fetch if we have testingRunHexId (indicates this is a testing report, not issues report)
        if (testingRunHexId && testingRunResultSummary) {
            const reportTestConfigurationData = await fetchReportTestConfigurationData(testingRunHexId, testingRunResultSummary, subCategoryMap)
            if (reportTestConfigurationData) {
                setReportTestConfigurationData(reportTestConfigurationData)
            }
        }

        const vulMap = reportTransform.createVulnerabilityMap(vulnerableTestingRunResults, categoryMap, subCategoryMap)
        setSeveritiesCount(vulMap.severitiesCount)
        setTotalApisTested(vulMap.totalApisTested)
        setTotalIssues(vulMap.totalIssues)
        setCategoryMap(vulMap.categoryMap)
        setCategoryVsIssuesMap(vulMap.categoryVsIssuesMap)
        setCategoryVsApisCountMap(vulMap.categoryVsApisCountMap)
        setTopCategoriesChartData(vulMap.topCategoriesChartData)
        setAktoFindingsTableData(vulMap.aktoFindingsTableData)
        setAktoRecommendations(vulMap.aktoRecommendations)
        setHostNameVsSeverityMap(vulMap.hostNameVsSeverityMap)

        const severityMapRes = reportTransform.createVulnerableAPIsSeverity(vulnerableTestingRunResults)
        setSeverityMap(severityMapRes)
    }

    useEffect(() => {
        setLoading(true)
        setPdfDownloadEnabled(false)
        fetchVulnerableData()
        setLoading(false)
        setPdfDownloadEnabled(true)
    }, [])


    const handleDownloadPF = async () => {
        const WAIT_DURATION = 5000, MAX_RETRIES = 60
        const reportUrl = window.location.href
        
        let pdfError = ""
        let status
        let pdf

        setPdfDownloadEnabled(false)

        let reportToastInterval = setInterval(() => {
            func.setToast(true, false, "Preparing your report. This might take a moment...")
        }, 1000)

        let generationStarted = false
        setTimeout(() => {
            clearInterval(reportToastInterval)
            generationStarted = true
            if(status === "IN_PROGRESS") {
                reportToastInterval = setInterval(() => {
                    func.setToast(true, false, "Report PDF generation in progress. Please wait...")
                }, 1000)
            }
        }, 6000)

        try {
            // Trigger pdf download
            const startDownloadReponse = await api.downloadReportPDF(null, organizationName, currentDate, reportUrl, userName, true)
            const reportId = startDownloadReponse?.reportId
            status = startDownloadReponse?.status
            pdf = startDownloadReponse?.pdf

            if (reportId !== null && status === "IN_PROGRESS") {
                // Poll for PDF completion
                for(let i = 0; i < MAX_RETRIES; i++) {
                    const pdfPollResponse = await api.downloadReportPDF(reportId, organizationName, currentDate, reportUrl, userName, false)
                    status = pdfPollResponse?.status

                    if (status === "COMPLETED") {
                        pdf = pdfPollResponse?.pdf
                        break
                    } else if (status === "ERROR") {
                        pdfError = "Failed to download PDF"
                        break
                    }

                    await func.sleep(WAIT_DURATION)

                    func.setToast(generationStarted, false, "Report PDF generation in progress. Please wait...")

                    if(i === MAX_RETRIES - 1) {
                        pdfError = "Failed to download PDF. The size might be too large. Filter out unnecessary issues and try again."
                    }
                }
            } else {
                if(status !== "COMPLETED") {
                    pdfError = "Failed to start PDF download"
                }
            }
        } catch (err) {
            pdfError = err?.response?.data?.actionErrors?.[0] || err.message
        }

        clearInterval(reportToastInterval)

        if (status === "COMPLETED") {
            if (pdf === undefined) {
                pdfError = "Failed to download PDF"
            }
            else {
                // Download the PDF
                try {
                    const byteCharacters = atob(pdf);
                    const byteNumbers = new Array(byteCharacters.length);
                    for (let i = 0; i < byteCharacters.length; i++) {
                        byteNumbers[i] = byteCharacters.charCodeAt(i);
                    }
                    const byteArray = new Uint8Array(byteNumbers);
                    const blob = new Blob([byteArray], { type: "application/pdf" });
                    const link = document.createElement("a");
                    link.href = window.URL.createObjectURL(blob);
                    link.setAttribute("download", "akto_security_findings.pdf");
                    document.body.appendChild(link);
                    link.click();
                    func.setToast(true, false, "Report PDF downloaded.")
                } catch (err) {
                    pdfError = err?.response?.data?.actionErrors?.[0] || err.message
                }
            }  
        }
        
        if (pdfError !== "") {
            func.setToast(true, true, `Error: ${pdfError}`)
        } 

        setPdfDownloadEnabled(true)
    }

    const reportSecondaryMenu = (
        <div className="report-header-css header-css" id="report-secondary-menu-container">
            <Box width="100%">
                <HorizontalStack align="space-between">
                    <VerticalStack>
                        <Text variant="headingXs">{organizationName} API Security Findings</Text>
                        <Text variant="bodySm">{currentDate}</Text>
                    </VerticalStack>
                    <div style={{ display: 'flex', alignItems: 'center' }}>
                        {
                            (window.USER_NAME?.toLowerCase()?.includes("@akto.io") || window.ACCOUNT_NAME?.toLowerCase()?.includes("advanced bank")) ? <Button primary onClick={() => handleDownloadPF()} disabled={!pdfDownloadEnabled}>Download</Button> : <></>
                        }
                        <img src='/public/white_logo.svg' alt="Logo" className='top-bar-logo' />
                    </div>
                </HorizontalStack>
            </Box>
        </div>
    )

    const reportTopBar = (
        <TopBar secondaryMenu={reportSecondaryMenu} />
    )

    const reportSummaryItems = [
        {
            title: "Vulnerable APIs found",
            data: totalApisTested,
        },
        {
            title: "Vulnerable Issues",
            data: totalIssues
        },
        {
            title: "Scan Duration",
            data: window.location.pathname.includes('issues') ? '-' : scanTime,
            visible: !window.location.pathname.includes('issues')
        }
    ]

    const SectionDivider = (beforeFooter) => {

        return (
            <>
                {beforeFooter === false ? (<div className="report-page-break"></div>) : null}
                <Box paddingInlineStart={5} paddingInlineEnd={5}>
                    <Divider />
                </Box>
            </>
        )
    }

    const reportTOCComponent = (
        <>
            <ReportTOC tocList={tocList}/>
            <SectionDivider beforeFooter={false} />
        </>
    )

    const reportSummaryComponent = (
        <>
            <ReportSummary
                reportSummaryItems={reportSummaryItems}
                severityMap={severityMap}
                topCategoriesChartData={topCategoriesChartData}
                organizationName={organizationName}
                currentDate={currentDate}
                reportTestConfigurationData={reportTestConfigurationData}
                hostNameVsSeverityMap={hostNameVsSeverityMap}
                summaryMode={summaryMode}
            />
            {summaryMode === false ? <SectionDivider beforeFooter={false} /> : null}
        </>
    )

    const reportFindingsComponent = (
        <>
            <ReportFindings
                aktoFindingsTableData={aktoFindingsTableData}
                categoryMap={categoryMap}
                categoryVsApisCountMap={categoryVsApisCountMap}
                categoryVsIssuesMap={categoryVsIssuesMap}
                organizationName={organizationName}
                subCategoryMap={subCategoryMap}
            />
            <SectionDivider beforeFooter={false} />
        </>
    )

    const reportConclusionComponent = (
        <>
            <ReportConclusion aktoRecommendations={aktoRecommendations} />
        </>
    )

    return (
        <Frame topBar={reportTopBar}>
            <div ref={pdfRef} id="report-container">
                <Box background="bg">
                    <ReportHeader userName={userName} organizationName={organizationName} currentDate={currentDate} setUserName={setUserName} setOrganizationName={setOrganizationName} />
                    
                    {summaryMode === false ? reportTOCComponent : null}

                    {reportSummaryComponent}

                    {summaryMode === false ? reportFindingsComponent : null}

                    {summaryMode === false ? reportConclusionComponent : null}

                    <SectionDivider beforeFooter={true} />
       
                    <ReportFooter />
                </Box>
            </div>
        </Frame>
    )
}

export default VulnerabilityReport